﻿/**
 * @file service_token_mgr.hpp
 * @date 2021/06/17 15:06
 *
 * @author harrybao (harrybao@123u.com)
 *
 * @brief 服务凭证管理
 *
 * @version v1.0.0
 *
 * @copyright Copyright © 2021 All Rights Reserved 上海欢乐互娱网络科技有限公司
 *
 * @par history:
 * <table>
 * <tr><th>Date                     <th>Version     <th>Author <th>Description
 * <tr><td>2021/06/17 15:06         <td>1.0.0       <td>harrybao
 * <td>创建初始版本
 * </table>
 */

#pragma once

#include <unordered_map>

#include "guild/guild.h"
#include "guild/guild_loader.hpp"
#include "guild/guild_saver.hpp"

#include "rpc_defines.h"

namespace guild_service {
namespace detail {

static token_type GetToken(const rpc::Address &address);

/**
 * @brief 服务凭证管理器
 *
 */
class ServiceTokenMgr final {
 public:
  /**
   * @brief 获取角色唯一id
   * 
   * @param service_name 
   * @param token 
   * @param role_uuid 
   * @return true 
   * @return false 
   */
  bool GetRoleUuid(const std::string &service_name, const token_type token,
                   role_uuid_type &role_uuid) const {
    if (token == kInvalidToken) {
      return false;
    }

    if (!detail::LoadRoleUuid(service_name, token, role_uuid)) {
      return false;
    }

    return true;
  }

  /**
   * @brief 注册
   *
   * @param service_name 服务名
   * @param call rpc请求
   * @param arg 请求数据
   * @param role_uuid 角色唯一id
   * @return true 注册成功
   * @return false 注册失败
   */
  bool Register(const std::string &service_name, rpc::StubCallPtr call,
                const std::string &account, const std::string &passwd,
                role_uuid_type &role_uuid) {
    // 1. account + passwd -> role_uuid
    // 2. token -> role_uuid
    // 3. <- token, role_uuid
    
    role_uuid = kInvalidRoleUuid;

    token_type token = kInvalidToken;

    if (!call) {
#ifdef DEBUG
      role_uuid = detail::TestGetRoleUuid(account, passwd);
      token = detail::TestGetToken(account, passwd);
      if (!detail::SaveTokenRoleUuid(service_name, kRedisSetOp, token, role_uuid)) {
        LOG_FAIL(
            "保存凭证映射角色唯一id失败 服务名:%s token:%u 角色唯一id:%llu",
            service_name.c_str(), token, role_uuid);
        return false;
      }
      return true;
#else
      return false;
#endif // DEBUG
    }

    if (!call->getTransport()) {
      return false;
    }

    if (account.empty()) {
      return false;
    }

    if (!detail::LoadRoleUuid(service_name, account, passwd, role_uuid)) {
      return false;
    }

    const auto &peer_address = call->getTransport()->getPeerAddress();
    LOG_DEBUG("peer_address ip:%s port:%u", peer_address.ip.c_str(), peer_address.port);

    const auto &local_address = call->getTransport()->getLocalAddress();
    LOG_DEBUG("local_address ip:%s port:%u", local_address.ip.c_str(), local_address.port);

    token = GetToken(peer_address);
    if (token == kInvalidToken) {
      return false;
    }
    
    // TODO check old token

    if (!detail::SaveTokenRoleUuid(service_name, kRedisSetOp, token, role_uuid)) {
      LOG_FAIL("保存凭证映射角色唯一id失败 服务名:%s token:%u 角色唯一id:%llu",
               service_name.c_str(), token, role_uuid);
      return false;
    }

    return true;
  }

  /**
   * @brief 注销
   * 
   * @param service_name 服务名
   * @param token 凭证
   * @return true 注销成功
   * @return false 注销失败
   */
  bool Unregister(const std::string &service_name, const token_type token) {
    if (!detail::SaveTokenRoleUuid(service_name, kRedisDelOp, token,
                                   kInvalidRoleUuid)) {
      return false;
    }

    return true;
  }
};

/// 服务凭证管理器单例
#define ServiceTokenMgrSingleton(...) \
  (*common::singleton<detail::ServiceTokenMgr>::instance(__VA_ARGS__))

/**
 * @brief 获取token
 * 
 * (hash ip) | port
 * @param address 地址 
 * @return token_type 凭证
 */
static token_type GetToken(const rpc::Address &address) {
  return (static_cast<token_type>(std::hash<std::string>{}(address.ip) << 6)) |
         address.port;
}

/**
 * @brief 获取token
 * 
 * @param call rpc请求
 * @return token_type 凭证
 */
static token_type GetToken(const rpc::StubCallPtr call) {
  if (!call) {
    return kInvalidToken;
  }
  if (!call->getTransport()) {
    return kInvalidToken;
  }
  return GetToken(call->getTransport()->getPeerAddress());
}

/**
 * @brief 获取角色唯一id
 * 
 * @param token 凭证
 * @return role_uuid_type 角色唯一id
 */
static role_uuid_type GetRoleUuid(const std::string &service_name,
                                  const token_type token) {
  if (token == kInvalidToken) {
    return kInvalidRoleUuid;
  }
  role_uuid_type role_uuid = kInvalidRoleUuid;
  ServiceTokenMgrSingleton().GetRoleUuid(service_name, token,
                                         role_uuid);
  return role_uuid;
}

}  // namespace detail
}  // namespace guild_service